/*

This file is part of Rooms3D.

Copyright (C) 2008 Pixecur Technologies Incorporated (Esotera).
Visit Esotera at http://www.esoteras3.com
 
Rooms3D is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
 
Rooms3D is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with Rooms3D; see the file COPYING.  If not, write to 
Pixecur Technologies, 213-31 Peet Street, St. John's, NL, A1B 3W8. 
 
Linking this library statically or dynamically with other modules is
making a combined work based on this library.  Thus, the terms and
conditions of the GNU General Public License cover the whole
combination.
 
 */
package com.datasentinel.rooms.ui.sh3d;

import java.awt.Color;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Image;
import java.awt.Point;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Logger;

import javax.imageio.ImageIO;
import javax.media.j3d.Appearance;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.ColoringAttributes;
import javax.media.j3d.Font3D;
import javax.media.j3d.FontExtrusion;
import javax.media.j3d.Material;
import javax.media.j3d.Node;
import javax.media.j3d.PickInfo;
import javax.media.j3d.Shape3D;
import javax.media.j3d.Text3D;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.TransparencyAttributes;
import javax.vecmath.Color3f;

import com.datasentinel.rooms.file.sh3d.FilePieceOfFurnitureTextureManager;
import com.datasentinel.rooms.file.sh3d.HomeFilePieceOfFurniture;
import com.datasentinel.rooms.j3d.util.ImagePlaneShape3D;
import com.datasentinel.rooms.j3d.util.PlaneShape3D;
import com.datasentinel.util.thumbnail.ThumbnailException;
import com.eteks.sweethome3d.model.HomePieceOfFurniture;
import com.sun.j3d.utils.universe.PlatformGeometry;

/**
 * @author Rick Power
 * 
 */
public class HoverController
{
	final private Logger logger = Logger.getLogger(HoverController.class.getName());
	private Canvas3D canvas3D;
	private PlatformGeometry platformGeometry;
	private HoverUI hoverUI;
	private static Image temporaryFileImage;
	private static Image binaryFileImage;
	private Timer timer;
	private TimerTask activateHover;
	private TimerTask deactivateHover;
	private TimerTask updateImage;
	private TimerTask checkActivate;
	private HomePieceOfFurniture hoveringOver;
	private Point mousePosition;
	private boolean isActive = false;
	static
	{
		try
		{
			temporaryFileImage = ImageIO
					.read(FilePieceOfFurnitureTextureManager.class
							.getResource("file_temporary.png"));
			binaryFileImage = ImageIO
					.read(FilePieceOfFurnitureTextureManager.class
							.getResource("binary.png"));
		}
		catch (IOException ioe)
		{
			throw new RuntimeException(ioe);
		}
	}

	HoverController(Canvas3D newCanvas3D, PlatformGeometry newPlatformGeometry)
	{
		super();
		canvas3D = newCanvas3D;
		platformGeometry = newPlatformGeometry;
		hoverUI = new HoverUI();
		timer = new Timer();
		mousePosition = new Point();
	}

	private void updateHoverInfo(final HomePieceOfFurniture homePiece)
	{
		hoverUI.setTransparency(0.8f);
		hoverUI.setImage(temporaryFileImage, homePiece.getName());
		if (updateImage != null)
			updateImage.cancel();
		updateImage = new TimerTask() {
			public void run()
			{
				java.awt.Image image = null;
				if (homePiece instanceof HomeFilePieceOfFurniture)
				{
					HomeFilePieceOfFurniture filePiece = (HomeFilePieceOfFurniture)homePiece;
					try
					{
						image = FilePieceOfFurnitureTextureManager
								.getInstance()
								.getThumbnail(filePiece.getFile());
					}
					catch (ThumbnailException te)
					{
					}
				}
				else
				{
					InputStream is = null;
					try
					{
						is = homePiece.getIcon().openStream();
						image = ImageIO.read(is);
						is.close();
					}
					catch (IOException ioe)
					{
					}
					finally
					{
						if (is != null)
							try
							{
								is.close();
							}
							catch (IOException ioe)
							{
							}
					}
				}
				if (image == null)
					image = binaryFileImage;
				hoverUI.setImage(image, homePiece.getName());
			}
		};
		timer.schedule(updateImage, 0L);
	}

	private synchronized void setHoveringOver(HomePieceOfFurniture homePiece)
	{
		if(hoverUI.getParent() != null && homePiece == null)
			return;
		hoveringOver = homePiece;
	}

	public synchronized void update(PickInfo pickInfo, final HomePieceOfFurniture newHomePiece,
			Point newMousePosition)
	{
		mousePosition.setLocation(newMousePosition);
		boolean isAboveHoverUI = false; 
		boolean hoverDefeat = false;
		
		if (pickInfo != null && pickInfo.getNode() != null)
			for (Node node = pickInfo.getNode().getParent(); node != null; node = node.getParent())
				if (node == hoverUI)
					isAboveHoverUI = true;

		if (null != newHomePiece)
		{
			if (!newHomePiece.equals(hoveringOver))
			{
				logger.fine("hoveringOver 1 = " + hoveringOver + ", isActive = " + isActive);
				if (null != checkActivate)
					checkActivate.cancel();
				if (null != deactivateHover)
					deactivateHover.cancel();
				setHoveringOver(newHomePiece);
				if (null == hoverUI.getParent())
				{
					activateHover = new TimerTask() {
						public void run()
						{
							if (newHomePiece.equals(hoveringOver))
							{
								if (null == hoverUI.getParent())
									platformGeometry.addChild(hoverUI);
								updateHoverInfo(newHomePiece);
							}
						}
					};
					timer.schedule(activateHover, 500L);
				}
				else
				{
					logger.fine("hoveringOver 2 = " + hoveringOver + ", isActive = " + isActive);
					updateHoverInfo(newHomePiece);
				}
			}
			else if(hoveringOver != null)
			{
				logger.fine("hoveringOver 3 = " + hoveringOver + ", isActive = " + isActive);
				checkActivate = new TimerTask(){
					public void run()
					{
						if(newHomePiece.equals(hoveringOver))
						{
							activate();
						}
					}
				};
				timer.schedule(checkActivate, 750L);
			}
		}
		else if(null != hoverUI.getParent())
		{
			logger.fine("not hoveringOver = " + hoveringOver + ", isActive = " + isActive);
			if (isAboveHoverUI)
			{
				logger.fine("hovering over hoverUI, deactivateHover = " + deactivateHover);
				if (null != deactivateHover)
					deactivateHover.cancel();
				deactivateHover = null;
			}
			else
			{
				logger.fine("not hovering over hoverUI, hoveringOver = " + hoveringOver + ", isActive = " + isActive);
				if (hoveringOver != null)
				{
					if (!isActive) 
						setHoveringOver(null);
					else 
					{
						hoverDefeat = true;
					}
				}
			}
		}
		else
		{
			hoverDefeat = true;
		}
		
		if (hoverDefeat && isActive /*&& deactivateHover == null*/)
		{
			deactivateHover = new TimerTask() {
				public void run()
				{
					logger.fine("cancelHover = " + hoveringOver);
					cancelHover();
					deactivateHover = null;
				}};
			timer.schedule(deactivateHover, 1250L);			
		}
			
	}

	public boolean isActive()
	{
		return isActive;
	}

	public synchronized void activate()
	{
		hoverUI.setTransparency(0);
		isActive = true;
	}

	public synchronized void cancelHover()
	{
		isActive = false;
		if (updateImage != null)
			updateImage.cancel();
		hoveringOver = null;
		hoverUI.detach();
	}

	public HomePieceOfFurniture hoveringOver()
	{
		return hoveringOver;
	}

	private static class HoverUI extends BranchGroup
	{
		private Transform3D hoverT3D;
		private ImagePlaneShape3D imagePlane;
		private PlaneShape3D popupPlane;
		private Text3D textGeometry;
		private Shape3D text;
		private Appearance textAppearance;
		private ImagePlaneShape3D closeButton;
		private TransparencyAttributes transparencyAttributes;

		public HoverUI()
		{
			super();
			setCapability(BranchGroup.ALLOW_PICKABLE_WRITE);
			setCapability(BranchGroup.ALLOW_DETACH);
			textAppearance = new Appearance();
			Material textMaterial = new Material();
			textAppearance.setMaterial(textMaterial);
			ColoringAttributes textColor = new ColoringAttributes();
			textColor.setColor(new Color3f(Color.decode("0x00446688")));
			textAppearance.setColoringAttributes(textColor);
			Font3D font3D = new Font3D(Font.decode("dialog-BOLD"),
					new FontExtrusion());
			textGeometry = new Text3D(font3D, "This is a test");
			textGeometry.setCapability(Text3D.ALLOW_STRING_WRITE);
			text = new Shape3D(textGeometry, textAppearance);
			text.setCapability(Shape3D.ALLOW_GEOMETRY_WRITE);
			text.setCapabilityIsFrequent(Shape3D.ALLOW_GEOMETRY_WRITE);
			popupPlane = new PlaneShape3D();
			imagePlane = new ImagePlaneShape3D();
			closeButton = new ImagePlaneShape3D();
			URL cancel = getClass().getResource("cancel.png");
			try
			{
				closeButton.setImage(ImageIO.read(cancel),
						Frame.getWindows()[0]);
			}
			catch (Exception e)
			{
				System.out.println(e);
			}
			textAppearance.getMaterial().setLightingEnable(false);
			imagePlane.getAppearance().getMaterial().setLightingEnable(false);
			popupPlane.getAppearance().getMaterial().setLightingEnable(false);
			closeButton.getAppearance().getMaterial().setLightingEnable(false);
			transparencyAttributes = new TransparencyAttributes();
			transparencyAttributes
					.setCapability(TransparencyAttributes.ALLOW_VALUE_WRITE);
			transparencyAttributes
					.setTransparencyMode(TransparencyAttributes.BLENDED);
			transparencyAttributes.setTransparency(0.5f);
			textAppearance.setTransparencyAttributes(transparencyAttributes);
			imagePlane.getAppearance().setTransparencyAttributes(
					transparencyAttributes);
			closeButton.getAppearance().setTransparencyAttributes(
					transparencyAttributes);
			popupPlane.getAppearance().setTransparencyAttributes(
					transparencyAttributes);
			hoverT3D = new Transform3D();
			hoverT3D.setTranslation(new javax.vecmath.Vector3f(-2.5f, 0, -14f));
			Transform3D cancelT3D = new Transform3D();
			cancelT3D.setScale(0.25);
			cancelT3D.setTranslation(new javax.vecmath.Vector3f(6f, 0, 0.05f));
			Transform3D textT3D = new Transform3D();
			textT3D
					.setTranslation(new javax.vecmath.Vector3f(-0.5f, -0.25f, 0));
			textT3D.setScale(0.029);
			Transform3D popupT3D = new Transform3D();
			popupT3D.setScale(new javax.vecmath.Vector3d(4, 0.5, 0));
			popupT3D.setTranslation(new javax.vecmath.Vector3f(2.5f, 0, 0));
			Transform3D imageT3D = new Transform3D();
			imageT3D.setScale(0.4);
			imageT3D
					.setTranslation(new javax.vecmath.Vector3f(-0.9f, 0, 0.05f));
			TransformGroup popupTG = new TransformGroup();
			TransformGroup imageTG = new TransformGroup();
			TransformGroup textTG = new TransformGroup();
			TransformGroup cancelTG = new TransformGroup();
			TransformGroup hoverTG = new TransformGroup();
			hoverTG.setCapability(TransformGroup.ALLOW_PICKABLE_WRITE);
			imageTG.setTransform(imageT3D);
			cancelTG.setTransform(cancelT3D);
			textTG.setTransform(textT3D);
			popupTG.setTransform(popupT3D);
			hoverTG.setTransform(hoverT3D);
			cancelTG.addChild(closeButton);
			textTG.addChild(text);
			imageTG.addChild(imagePlane);
			popupTG.addChild(popupPlane);
			hoverTG.addChild(popupTG);
			hoverTG.addChild(textTG);
			hoverTG.addChild(imageTG);
			hoverTG.addChild(cancelTG);
			javax.media.j3d.OrderedGroup og = new javax.media.j3d.OrderedGroup();
			og.addChild(hoverTG);
			addChild(og);
		}

		public void setImage(Image image, String string)
		{
			imagePlane.setImage(image, Frame.getWindows()[0]);
			textGeometry.setString(string.length() > 36?string.substring(0, 32)
					+ "...":string);
		}

		public void setTransparency(float transparency)
		{
			transparencyAttributes.setTransparency(transparency);
		}
	}
}